/*
 * Copyright 2015 MingxingWang

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.audaque.graphdb

import java.io.{DataInputStream, DataOutputStream}

import scala.collection.mutable

/**
 * Created by Ming on 2015/3/26.
 */
object Type {

  val types: mutable.Map[Int, Type] = new mutable.HashMap[Int, Type]()

  def apply(tid: Int, clazz: Class[_]): Type = {
    val t = new Type(tid, clazz)
    types += (tid -> t)
    t
  }

  def apply(tid: Int): Type = {
    types.get(tid) match {
      case Some(t) => t
      case None => null.asInstanceOf[Type]
    }
  }

  val ID_Int: Int = 0
  val ID_Long: Int = 1
  val ID_Float: Int = 2
  val ID_Double: Int = 3
  val ID_Short: Int = 4
  val ID_Char: Int = 5
  val ID_Boolean: Int = 6
  val ID_Byte: Int = 7
  val ID_String: Int = 8
  val ID_UnKnown: Int = 1000000
  val Int: Type = Type(ID_Int, classOf[java.lang.Integer])
  val Long: Type = Type(ID_Long, classOf[java.lang.Long])
  val Float: Type = Type(ID_Float, classOf[java.lang.Float])
  val Double: Type = Type(ID_Double, classOf[java.lang.Double])
  val Short: Type = Type(ID_Short, classOf[java.lang.Short])
  val Char: Type = Type(ID_Char, classOf[java.lang.Character])
  val Boolean: Type = Type(ID_Boolean, classOf[java.lang.Boolean])
  val Byte: Type = Type(ID_Byte, classOf[java.lang.Byte])
  val String: Type = Type(ID_String, classOf[String])
  val UnKnown: Type = Type(ID_UnKnown, classOf[Any])

}

class Type(val tid: Int, val clazz: Class[_]) extends Serializable {

  def containValue(value: Any): Boolean = {
    value == null || value.getClass == clazz
  }

  def string2SuitableValue(in: String): Any = {
    if (in == null) return in
    tid match {
      case Type.ID_Int => in.toInt
      case Type.ID_Long => in.toLong
      case Type.ID_Float => in.toFloat
      case Type.ID_Double => in.toDouble
      case Type.ID_Short => in.toShort
      case Type.ID_Char => in.charAt(0)
      case Type.ID_Boolean => in.toBoolean
      case Type.ID_Byte => in.toByte
      case _ => in
    }
  }

  trait Reader extends Any with Serializable {
    def read(is: DataInputStream): Any
  }

  val reader: Reader = {
    tid match {
      case Type.ID_Int => new Reader() {
        def read(is: DataInputStream): Any = is.readInt()
      }
      case Type.ID_Long => new Reader() {
        def read(is: DataInputStream): Any = is.readLong()
      }
      case Type.ID_Float => new Reader() {
        def read(is: DataInputStream): Any = is.readFloat()
      }
      case Type.ID_Double => new Reader() {
        def read(is: DataInputStream): Any = is.readDouble()
      }
      case Type.ID_Short => new Reader() {
        def read(is: DataInputStream): Any = is.readShort()
      }
      case Type.ID_Char => new Reader() {
        def read(is: DataInputStream): Any = is.readChar()
      }
      case Type.ID_Boolean => new Reader() {
        def read(is: DataInputStream): Any = is.readBoolean()
      }
      case Type.ID_Byte => new Reader() {
        def read(is: DataInputStream): Any = is.readByte()
      }
      case Type.ID_String => new Reader() {
        def read(is: DataInputStream): Any = is.readUTF()
      }
      case _ =>
        new Reader() {
          def read(is: DataInputStream): Any = null
        }
    }
  }

  trait Writer extends Any with Serializable {
    def write(value: Any, os: DataOutputStream)
  }

  val writer: Writer = {
    tid match {
      case Type.ID_Int => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeInt(value.asInstanceOf[Int])
        }
      }
      case Type.ID_Long => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeLong(value.asInstanceOf[Long])
        }
      }
      case Type.ID_Float => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeFloat(value.asInstanceOf[Float])
        }
      }
      case Type.ID_Double => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeDouble(value.asInstanceOf[Double])
        }
      }
      case Type.ID_Short => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeShort(value.asInstanceOf[Short])
        }
      }
      case Type.ID_Char => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeChar(value.asInstanceOf[Char])
        }
      }
      case Type.ID_Boolean => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeBoolean(value.asInstanceOf[Boolean])
        }
      }
      case Type.ID_Byte => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeByte(value.asInstanceOf[Byte])
        }
      }
      case Type.ID_String => new Writer() {
        def write(value: Any, os: DataOutputStream) {
          os.writeUTF(value.asInstanceOf[String])
        }
      }
      case _ =>
        new Writer() {
          def write(value: Any, os: DataOutputStream) {}
        }
    }
  }
}